package EP.MetricasContenido;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
* @class OkapiBM25
* @author Mauricio Alvarez.
* @brief Esta clase representa la clase OkapiBM25, correspondiente a una entidad principal (EP).
*/
public class OkapiBM25
{

    /**
     *  @var int id_pagina
     *  @brief identificador de la página en la BD.
    */
    private int id_pagina;
    /**
     *  @var int id_relacion_pag
     *  @brief identificador de la tabla RELACION_PAGINA en la BD.
    */
    private int id_relacion_pag;
    /**
     *  @var int pag_id_pagina
     *  @brief identificador de una página origen en la BD.
    */
    private int pag_id_pagina;
    /**
     *  @var String Termino
     *  @brief Termino correspondiente a la consulta Q={q1,q2,...,qn}.
    */
    private String Termino;
    /**
     *  @var float TFij_SN
     *  @brief Frecuencia del termino i en la pagina j. (SIN NORMALIZAR)
    */
    private double TFij_SN = 0;
    /**
     *  @var float TFij_N
     *  @brief Frecuencia del termino i en la pagina j (NORMALIZADO).
    */
    private double TFij_N = 0;
    /**
     *  @var float TFij_S
     *  @brief Frecuencia del termino i en la pagina j (SUAVISADO).
    */
    private double TFij_S = 0;
    /**
     *  @var private double IDF
     *  @brief Valor del IDF para el lote de páginas obtenidas de la BD.
    */
    private double IDF = 0;
    /**
     *  @var private double IDF_O
     *  @brief Valor del IDF (Okapi BM25) para el lote de páginas obtenidas de la BD.
    */
    private double IDF_O = 0;
    /**
     *  @var private double TfxIDF_SN
     *  @brief Valor del TfxIDF_SN para el lote de páginas obtenidas de la BD. (SIN NORMALIZAR)
    */
    private double TfxIDF_SN = 0;
     /**
     *  @var private double TfxIDF_N
     *  @brief Valor del TfxIDF_N para el lote de páginas obtenidas de la BD. (NORMALIZADO)
    */
    private double TfxIDF_N = 0;
    /**
     *  @var private double TfxIDF_S
     *  @brief Valor del TfxIDF_S para el lote de páginas obtenidas de la BD. (SUAVISADO)
    */
    private double TfxIDF_S = 0;
    /**
     *  @var private double TfxIDF_O_SN
     *  @brief Valor del TfxIDF_O_SN para el lote de páginas obtenidas de la BD. (PROBABILISTICO)
    */
    private double TfxIDF_O_SN = 0;
    /**
     *  @var private double TfxIDF_O_N
     *  @brief Valor del TfxIDF_O_N para el lote de páginas obtenidas de la BD. (PROBABILISTICO)
    */
    private double TfxIDF_O_N = 0;
    /**
     *  @var private double TfxIDF_O_S
     *  @brief Valor del TfxIDF_O_S para el lote de páginas obtenidas de la BD. (PROBABILISTICO)
    */
    private double TfxIDF_O_S = 0;
    /**
     *  @var private double Okapi
     *  @brief Valor del Okapi para el lote de páginas obtenidas de la BD. (PROBABILISTICO)
    */
    private double Okapi = 0;
    /**
     *  @var private OkapiBM25 okapibm25
     *  @brief Variable global de tipo clase OkapiBM25.
    */
    private OkapiBM25 okapibm25;


    ////////////////////////
    // GETTER & SETTER
    ////////////////////////
    public double getIDF() {
        return IDF;
    }

    public void setIDF(double IDF) {
        this.IDF = IDF;
    }

    public double getIDF_O() {
        return IDF_O;
    }

    public void setIDF_O(double IDF_O) {
        this.IDF_O = IDF_O;
    }

    public double getOkapi() {
        return Okapi;
    }

    public void setOkapi(double Okapi) {
        this.Okapi = Okapi;
    }

    public double getTFij_N() {
        return TFij_N;
    }

    public void setTFij_N(double TFij_N) {
        this.TFij_N = TFij_N;
    }

    public double getTFij_S() {
        return TFij_S;
    }

    public void setTFij_S(double TFij_S) {
        this.TFij_S = TFij_S;
    }

    public double getTFij_SN() {
        return TFij_SN;
    }

    public void setTFij_SN(double TFij_SN) {
        this.TFij_SN = TFij_SN;
    }

    public String getTermino() {
        return Termino;
    }

    public void setTermino(String Termino) {
        this.Termino = Termino;
    }

    public double getTfxIDF_N() {
        return TfxIDF_N;
    }

    public void setTfxIDF_N(double TfxIDF_N) {
        this.TfxIDF_N = TfxIDF_N;
    }

    public double getTfxIDF_O_N() {
        return TfxIDF_O_N;
    }

    public void setTfxIDF_O_N(double TfxIDF_O_N) {
        this.TfxIDF_O_N = TfxIDF_O_N;
    }

    public double getTfxIDF_O_S() {
        return TfxIDF_O_S;
    }

    public void setTfxIDF_O_S(double TfxIDF_O_S) {
        this.TfxIDF_O_S = TfxIDF_O_S;
    }

    public double getTfxIDF_O_SN() {
        return TfxIDF_O_SN;
    }

    public void setTfxIDF_O_SN(double TfxIDF_O_SN) {
        this.TfxIDF_O_SN = TfxIDF_O_SN;
    }

    public double getTfxIDF_S() {
        return TfxIDF_S;
    }

    public void setTfxIDF_S(double TfxIDF_S) {
        this.TfxIDF_S = TfxIDF_S;
    }

    public double getTfxIDF_SN() {
        return TfxIDF_SN;
    }

    public void setTfxIDF_SN(double TfxIDF_SN) {
        this.TfxIDF_SN = TfxIDF_SN;
    }

    public int getId_pagina() {
        return id_pagina;
    }

    public void setId_pagina(int id_pagina) {
        this.id_pagina = id_pagina;
    }

    public int getId_relacion_pag() {
        return id_relacion_pag;
    }

    public void setId_relacion_pag(int id_relacion_pag) {
        this.id_relacion_pag = id_relacion_pag;
    }

    public int getPag_id_pagina() {
        return pag_id_pagina;
    }

    public void setPag_id_pagina(int pag_id_pagina) {
        this.pag_id_pagina = pag_id_pagina;
    }


    ////////////////////
    // OPERACIONES
    ////////////////////

    /**
     * @brief Calcula la métrica de BM25.
     * @param ResultadosTfxIDF, ResultadosTF, factorb, factorK1, OpcionContenido
     * @return Map<Integer, List>
    */
    public Map<Integer, List> CalcularOkapi(Map<Integer, List> ResultadosTfxIDF,
                                     Map<Integer, List> ResultadosTF,
                                     double factorb,
                                     double factorK1,
                                     int OpcionContenido)
    {


        ////////////////////////////////////////////////////////////////////////
        //PASO 0: Segun la selección de OpcionContenido (1:BODY/2:SURROUND TEXT)
        //        se calcula el tf:
        ////////////////////////////////////////////////////////////////////////

        //Atributos Clase:
        int id_pagina;
        int id_relacion_pagina = 0;
        String Termino;
        double TFij_SN = 0;
        double TFij_N = 0;
        double TFij_S = 0;
        double IDF = 0;
        double IDF_O = 0;
        double TfxIDF_SN = 0;
        double TfxIDF_N = 0;
        double TfxIDF_S = 0;
        double TfxIDF_O_SN = 0;
        double TfxIDF_O_N = 0;
        double TfxIDF_O_S = 0;


        ////////////
        // BODY   //
        ////////////
        if (OpcionContenido == 1)
        {

            System.out.println("***************************************");
            System.out.println("**   CALCULO DE OKAPI (BODY)         **");
            System.out.println("***************************************");



            //Atributos OKAPI:
            double Okapi = 0;//Resultado Okapi para la pagina j consulta q.
            double suma_avgdl = 0;
            double avgdl = 0;//es la longitud media de los documentos en la colección sobre la cual estamos realizando la búsqueda.
            double longitudPagina = 0;//Longitud de la página j.
            double numerador = 0;
            double denominador = 0;

            //Guarda los resultados de todos los estudios:
            Map<Integer, List> ResultadosOkapiBM25 = new HashMap();

            //////////////////////////////////////////////////////////
            //PASO 1: Para cada página en resultadosTF hacer:
            //////////////////////////////////////////////////////////

            //Guarda la longitudes de las paginas de la colección (Lote) en cantidad de terminos.
            //La key corresponde el id_pagina y el value a la cantidad de terminos
            Map<Integer, Double> LongitudesPagina = new HashMap<Integer, Double>();

            //Iterador del conjunto
            Set conjuntoResultadosTF = ResultadosTF.entrySet();
            Iterator iter = conjuntoResultadosTF.iterator();

            while(iter.hasNext())
            {
                //Se obtiene el par de ResultadosTF:
                Map.Entry par = (Map.Entry)iter.next();
                //Se obtiene el id_pagina:
                id_pagina = (Integer) par.getKey();
                //Se obtiene para esa página los resultados segun termino consulta:
                List<TF> resultadosPaginaTf = new ArrayList<TF>();
                resultadosPaginaTf = (List<TF>) par.getValue();
                //Como el largo del documento es el mismo para cualquier resultado
                //TF para cualquier termino se extrae desde el primero y se guarda
                //en el Map LongitudPagina:
                longitudPagina = resultadosPaginaTf.get(0).getNTDj_SN();
                LongitudesPagina.put(id_pagina, longitudPagina);
                //Se suman las longuitudes para calcular despues la media:
                suma_avgdl = suma_avgdl + longitudPagina;
            }

            //Se calcula la media de las longitudes de la coleccion avgdl:
            avgdl = suma_avgdl / Double.valueOf(LongitudesPagina.size());



            //////////////////////////////////////////////////////////
            //PASO 2: Para cada página con resultados TF-IDF hacer:
            //////////////////////////////////////////////////////////

            Set conjuntoResultadosTfxIDF = ResultadosTfxIDF.entrySet();
            Iterator it = conjuntoResultadosTfxIDF.iterator();

            while(it.hasNext())
            {
                //Se obtiene el par <id_pagina, ListadoTf-IDF>
                Map.Entry me = (Map.Entry)it.next();
                //Se obtiene el id_pagina:
                id_pagina = (Integer) me.getKey();
                //Se obtiene el listado TF-IDF:
                List<TfxIDF> ResultadosPaginaTfxIDF = new ArrayList();
                ResultadosPaginaTfxIDF = ResultadosTfxIDF.get(id_pagina);

                List<OkapiBM25> ResultadosTerminoOkapiBM25 = new ArrayList<OkapiBM25>();

                //Para cada página dentro de ResultadosPaginaTfxIDF hacer:
                for (int i=0; i<ResultadosPaginaTfxIDF.size(); i++)
                {
                    //Se obtienen los resultados TF-IDF de la página:
                    Termino     = ResultadosPaginaTfxIDF.get(i).getTermino();
                    TFij_SN     = ResultadosPaginaTfxIDF.get(i).getTFij_SN();
                    TFij_N      = ResultadosPaginaTfxIDF.get(i).getTFij_N();
                    TFij_S      = ResultadosPaginaTfxIDF.get(i).getTFij_S();
                    IDF         = ResultadosPaginaTfxIDF.get(i).getIDF();
                    IDF_O       = ResultadosPaginaTfxIDF.get(i).getIDF_O();
                    TfxIDF_SN   = ResultadosPaginaTfxIDF.get(i).getTfxIDF_SN();
                    TfxIDF_N    = ResultadosPaginaTfxIDF.get(i).getTfxIDF_N();
                    TfxIDF_S    = ResultadosPaginaTfxIDF.get(i).getTfxIDF_S();
                    TfxIDF_O_SN = ResultadosPaginaTfxIDF.get(i).getTfxIDF_O_SN();
                    TfxIDF_O_N  = ResultadosPaginaTfxIDF.get(i).getTfxIDF_O_N();
                    TfxIDF_O_S  = ResultadosPaginaTfxIDF.get(i).getTfxIDF_O_S();

                    //Se obtiene la longitud de la página evaluada del LongitudesPagina:
                    longitudPagina = LongitudesPagina.get(id_pagina);

                    //Se calcula OKAPI:
                    numerador = IDF_O * (TFij_SN*(factorK1 + 1));
                    denominador = (TFij_SN + factorK1*(1 - factorb + factorb * (longitudPagina/avgdl)));
                    Okapi = numerador / denominador;

                    //Se setean los atributos de la clase Okapi:
                    okapibm25 = new OkapiBM25();
                    okapibm25.setId_pagina(id_pagina);
                    okapibm25.setTermino(Termino);
                    okapibm25.setTFij_SN(TFij_SN);
                    okapibm25.setTFij_N(TFij_N);
                    okapibm25.setTFij_S(TFij_S);
                    okapibm25.setIDF(IDF);
                    okapibm25.setIDF_O(IDF_O);
                    okapibm25.setTfxIDF_SN(TfxIDF_SN);
                    okapibm25.setTfxIDF_N(TfxIDF_N);
                    okapibm25.setTfxIDF_S(TfxIDF_S);
                    okapibm25.setTfxIDF_O_SN(TfxIDF_O_SN);
                    okapibm25.setTfxIDF_O_N(TfxIDF_O_N);
                    okapibm25.setTfxIDF_O_S(TfxIDF_O_S);
                    okapibm25.setOkapi(Okapi);

                    //BORRAR ESTOOOOO!!!:!::!:!:!:!:!:!:!:!:!:!:!!:::!:!::!:!:!

                            System.out.println("------------------------------------");
                            System.out.println("id_pagina: "+ id_pagina);
                            System.out.println("termino: "+ Termino);
                            System.out.println("TFij_SN: "+ TFij_SN);
                            System.out.println("TFij_N: "+ TFij_N);
                            System.out.println("TF_S: "+ TFij_S);
                            System.out.println("IDF: "+ IDF);
                            System.out.println("IDF_O: "+ IDF_O);
                            System.out.println("TfxIDF_SN: "+ TfxIDF_SN);
                            System.out.println("TfxIDF_N: "+ TfxIDF_N);
                            System.out.println("TfxIDF_S: "+ TfxIDF_S);
                            System.out.println("TfxIDF_O_SN: "+ TfxIDF_O_SN);
                            System.out.println("TfxIDF_O_N: "+ TfxIDF_O_N);
                            System.out.println("TfxIDF_O_S: "+ TfxIDF_O_S);
                            System.out.println("Okapi: "+ Okapi);
                            System.out.println("++++++++++++++++++++++++++++++++++++");

                    //BORRAR ESTOOOOO!!!:!::!:!:!:!:!:!:!:!:!:!:!!:::!:!::!:!:!

                    //Se guarda en la estructura de clase todos los resultados:
                    ResultadosTerminoOkapiBM25.add(okapibm25);

                }
                //Cuando se calcula el okapi de cada termino para la página i, se
                //guardan todas ellas con el id_pagina en el Map:
                ResultadosOkapiBM25.put(id_pagina, (ArrayList) ResultadosTerminoOkapiBM25);

            }

            ///////////////////////////////////
            //PASO 3: Se retornan los Scores
            ///////////////////////////////////
            return ResultadosOkapiBM25;

        }//FIN IF BODY



        ////////////////////
        // SURROUND TEXT  //
        ////////////////////

        if (OpcionContenido == 2)
        {

            System.out.println("******************************************");
            System.out.println("**   CALCULO DE OKAPI (SURROUND TEXT)   **");
            System.out.println("******************************************");

            //Atributos OKAPI:
            double Okapi = 0;//Resultado Okapi para la pagina j consulta q.
            double suma_avgdl = 0;
            double avgdl = 0;//es la longitud media de los documentos en la colección sobre la cual estamos realizando la búsqueda.
            double longitudPagina = 0;//Longitud de la página j.
            double numerador = 0;
            double denominador = 0;

            //Guarda los resultados de todos los estudios:
            Map<Integer, List> ResultadosOkapiBM25_ST = new HashMap();

            ///////////////////////////////////////////////////////////////
            //PASO 1: Para cada ST en cadapágina en resultadosTF hacer:
            ///////////////////////////////////////////////////////////////

            //Guarda la longitudes de los ST's de la colección (Lote) en
            //cantidad de terminos.
            //La key corresponde el id_relacion_pag y el value a la cantidad de
            //terminos
            Map<Integer, Double> LongitudesST = new HashMap();

            //Iterador del conjunto
            Set conjuntoResultadosTF = ResultadosTF.entrySet();
            Iterator iter = conjuntoResultadosTF.iterator();

            while(iter.hasNext())
            {
                //Se obtiene el par de ResultadosTF:
                Map.Entry par = (Map.Entry)iter.next();

                //Se obtiene el id_relacion_pagina:
                id_relacion_pagina = (Integer) par.getKey();

                //Se obtiene para ese ST los resultados segun termino consulta:
                List<TF> resultadosTf_ST = new ArrayList();
                resultadosTf_ST = (List<TF>) par.getValue();

                //Como el largo del documento es el mismo para cualquier resultado
                //TF para cualquier termino se extrae desde el primero y se guarda
                //en el Map LongitudPagina:
                longitudPagina = resultadosTf_ST.get(0).getNTDj_SN();
                LongitudesST.put(id_relacion_pagina, longitudPagina);

                //Se suman las longuitudes para calcular despues la media:
                suma_avgdl = suma_avgdl + longitudPagina;

            }

            //Se calcula la media de las longitudes de la coleccion avgdl:
            avgdl = suma_avgdl / Double.valueOf(LongitudesST.size());



            ////////////////////////////////////////////////////////////////////
            //PASO 2: Para cada ST en cada página con resultados TF-IDF hacer:
            ////////////////////////////////////////////////////////////////////

            Set conjuntoResultadosTfxIDF = ResultadosTfxIDF.entrySet();
            Iterator it = conjuntoResultadosTfxIDF.iterator();

            while(it.hasNext())
            {
                //Se obtiene el par <id_pagina, ListadoTf-IDF>
                Map.Entry me = (Map.Entry)it.next();
                //Se obtiene el id_pagina:
                id_relacion_pagina = (Integer) me.getKey();
                //Se obtiene el listado TF-IDF:
                List<TfxIDF> ResultadosPaginaTfxIDF = new ArrayList();
                ResultadosPaginaTfxIDF = ResultadosTfxIDF.get(id_relacion_pagina);

                List<OkapiBM25> ResultadosTerminoOkapiBM25 = new ArrayList();

                //Para cada página dentro de ResultadosPaginaTfxIDF hacer:
                for (int i=0; i<ResultadosPaginaTfxIDF.size(); i++)
                {
                    //Se obtienen los resultados TF-IDF de la página:
                    Termino     = ResultadosPaginaTfxIDF.get(i).getTermino();
                    TFij_SN     = ResultadosPaginaTfxIDF.get(i).getTFij_SN();
                    TFij_N      = ResultadosPaginaTfxIDF.get(i).getTFij_N();
                    TFij_S      = ResultadosPaginaTfxIDF.get(i).getTFij_S();
                    IDF         = ResultadosPaginaTfxIDF.get(i).getIDF();
                    IDF_O       = ResultadosPaginaTfxIDF.get(i).getIDF_O();
                    TfxIDF_SN   = ResultadosPaginaTfxIDF.get(i).getTfxIDF_SN();
                    TfxIDF_N    = ResultadosPaginaTfxIDF.get(i).getTfxIDF_N();
                    TfxIDF_S    = ResultadosPaginaTfxIDF.get(i).getTfxIDF_S();
                    TfxIDF_O_SN = ResultadosPaginaTfxIDF.get(i).getTfxIDF_O_SN();
                    TfxIDF_O_N  = ResultadosPaginaTfxIDF.get(i).getTfxIDF_O_N();
                    TfxIDF_O_S  = ResultadosPaginaTfxIDF.get(i).getTfxIDF_O_S();

                    //Se obtiene la longitud de ST evaluada del
                    //LongitudesPagina:
                    longitudPagina = LongitudesST.get(id_relacion_pagina);

                    //Se calcula OKAPI:
                    numerador = IDF_O * (TFij_SN*(factorK1 + 1));
                    denominador = (TFij_SN + factorK1*(1 - factorb + factorb * (longitudPagina/avgdl)));
                    Okapi = numerador / denominador;

                    //Se setean los atributos de la clase Okapi:
                    okapibm25 = new OkapiBM25();
                    okapibm25.setId_relacion_pag(id_relacion_pagina);
                    okapibm25.setTermino(Termino);
                    okapibm25.setTFij_SN(TFij_SN);
                    okapibm25.setTFij_N(TFij_N);
                    okapibm25.setTFij_S(TFij_S);
                    okapibm25.setIDF(IDF);
                    okapibm25.setIDF_O(IDF_O);
                    okapibm25.setTfxIDF_SN(TfxIDF_SN);
                    okapibm25.setTfxIDF_N(TfxIDF_N);
                    okapibm25.setTfxIDF_S(TfxIDF_S);
                    okapibm25.setTfxIDF_O_SN(TfxIDF_O_SN);
                    okapibm25.setTfxIDF_O_N(TfxIDF_O_N);
                    okapibm25.setTfxIDF_O_S(TfxIDF_O_S);
                    okapibm25.setOkapi(Okapi);

                    //BORRAR ESTOOOOO!!!:!::!:!:!:!:!:!:!:!:!:!:!!:::!:!::!:!:!

                            System.out.println("------------------------------------");
                            System.out.println("id_relacion_pagina: "+ id_relacion_pagina);
                            System.out.println("termino: "+ Termino);
                            System.out.println("TFij_SN: "+ TFij_SN);
                            System.out.println("TFij_N: "+ TFij_N);
                            System.out.println("TF_S: "+ TFij_S);
                            System.out.println("IDF: "+ IDF);
                            System.out.println("IDF_O: "+ IDF_O);
                            System.out.println("TfxIDF_SN: "+ TfxIDF_SN);
                            System.out.println("TfxIDF_N: "+ TfxIDF_N);
                            System.out.println("TfxIDF_S: "+ TfxIDF_S);
                            System.out.println("TfxIDF_O_SN: "+ TfxIDF_O_SN);
                            System.out.println("TfxIDF_O_N: "+ TfxIDF_O_N);
                            System.out.println("TfxIDF_O_S: "+ TfxIDF_O_S);
                            System.out.println("Okapi: "+ Okapi);
                            System.out.println("++++++++++++++++++++++++++++++++++++");

                    //BORRAR ESTOOOOO!!!:!::!:!:!:!:!:!:!:!:!:!:!!:::!:!::!:!:!

                    //Se guarda en la estructura de clase todos los resultados:
                    ResultadosTerminoOkapiBM25.add(okapibm25);

                }
                //Cuando se calcula el okapi de cada termino para la página i, se
                //guardan todas ellas con el id_pagina en el Map:
                ResultadosOkapiBM25_ST.put(id_relacion_pagina, ResultadosTerminoOkapiBM25);

            }

            ///////////////////////////////////
            //PASO 3: Se retornan los Scores
            ///////////////////////////////////
            return ResultadosOkapiBM25_ST;

        }//FIN IF SURROUND TEXT

        return null;


    }//FIN CalcularOkapi


}//FIN CLASE OkapiBM25 /////////////////////////////////////////////////////////////////////////////////////